#include "Semantico.h"

Semantico::Semantico(NoArvore &mArvoreSintatica , HashIdentificadores & mHashVariaveis)
{
	this->mTipoCorrente = -1;// valor inicial
	this->mIsExpressao = false; // valor inicial
	this->mRetorno = -1;
	this->mExRetorno = -1;
	
	// recebe a arvore sintatica da analise sintatica
	this->mArvoreSintatica = &mArvoreSintatica;
	this->mHashVariaveis = &mHashVariaveis;
	ProcuraExpressao(this->mArvoreSintatica);
}

//operandos de uma expressão devem ser compatíveis com o operador.
void Semantico::ProcuraExpressao(NoArvore *mPai)
{
	// recebe os filhos do pai
	vector<NoArvore*> mFilhos = mPai->GetFilhos();
	
	
	// analisa cada filho
	for(int i = 0 ; i < mFilhos.size() ;	 i++)
	{
		
		//se existir ATRIBUICAO deve-se tratar
		if((mFilhos.at(i)->mostra().compare("ATRIBUICAO")) == 0)
		{		
			// analisa compatibilidade de operadores
			Atribuicao(mFilhos.at(i));
			// nao eh necessario procurar novamente abaixo do EXPRESSAO
			continue;
		}else
		{
			// COMANDO IF,  EXPRESSAO deve ser boolean
			if((mFilhos.at(i)->mostra().compare("COMANDO CONDICIONAL")) == 0
			|| (mFilhos.at(i)->mostra().compare("COMANDO REPETITIVO")) == 0)
			{
				ExpreRetBool(mFilhos.at(i));
				// chama para COMANDO SE ROTULO dentro DO WHILE ou IF
				ProcuraExpressao(mFilhos.at(i)->GetFilhos().at(3));
				
				if(mFilhos.at(i)->GetFilhos().size() > 4)
				{

					//chama para ELSE se existir
					ProcuraExpressao(mFilhos.at(i)->GetFilhos().at(5));
				}
				continue;
			}
			
		}
		if( (mFilhos.at(i)->mostra().compare("EXPRESSAO") == 0) )
			{
				
				this->mIsExpressao = true;
				//CHAMADA PARA EXPRESSA DENTRO DO IF , FAZER DE FORMA MAIS LEGIVEL
				// OU , CHAMADA PARA EXPRESSA DENTRO DO WHILE , FAZER DE FORMA MAIS LEGIVEL
				this->Expressao(mFilhos.at(i));
				this->mIsExpressao = false;
				continue;
				 			 
				
			}
		
		
		// chamada para ananlise de procedimentos e funções
		if( (mFilhos.at(i)->mostra().compare("EXPRESSAO ENTRE PARENTESES") == 0) )
		{
			//for(int k = 0 ; k <mFilhos.at(i)->GetFilhos().size(); k++)
				//cout<<mFilhos.at(i)->GetFilhos().at(k)->mostra(); getchar();
			if( (mFilhos.at(i)->mostra().compare("EXPRESSAO") == 0) )
			{
				
				this->mIsExpressao = true;
				//CHAMADA PARA EXPRESSA DENTRO DO IF , FAZER DE FORMA MAIS LEGIVEL
				// OU , CHAMADA PARA EXPRESSA DENTRO DO WHILE , FAZER DE FORMA MAIS LEGIVEL
				this->Expressao(mFilhos.at(i));
				this->mIsExpressao = false;
				continue;
				 			 
				
			}
			
			//CHAMADA SEM PARAMETROS
			if(mFilhos.at(i)->GetFilhos().size() == 1)
			{	
				continue;
			}
			
			//RETORNA O METODO COM AS VARIAVEIS sempre num nivel acima
			// um procedimento sempre esta um nivel acima da chamada
			this->mNoCompara = mHashVariaveis->PesquisaVariavelNivel(mFilhos.at(i)->GetFilhos().at(0)->mostra(),mFilhos.at(i)->GetFilhos().at(1)->GetNivel()+1);	
			
			//analisa a expressao dentro da chamada de procedimento
			
			if(this->mNoCompara != NULL){
				
				cont = 0;
				// analisa cada filho
				AnalisaProcedimento(mFilhos.at(i));
				
				cont = 0;
				continue;
			}
			
		}
		
		
		
		
		ProcuraExpressao(mFilhos.at(i));
	}
	
}

void Semantico::AnalisaProcedimento(NoArvore *mPai)
{
	// recebe os filhos do pai
	vector<NoArvore*> mFilhos = mPai->GetFilhos();
	

	// analisa cada filho
	for(int i = 0 ; i < mFilhos.size() ;i++)
	{
			
				//se for um terminal deve-se ver o tipo
				if(mFilhos.at(i)->IsTerminal() == true )
				{
					
					// PESQUISAR NA HASH A VARIAVEL
					// saber qual o tipo lexico
					if (IsIdentificador(mFilhos.at(i)->GetTipoLexico(), mFilhos.at(i)->mostra() ) )
					{
						
						//analisar Nohash
						// RETORNO O TIPO DA VARIAVEL "BOOLEAN" , "INTEGER"
						 NoHash *mNohash = mHashVariaveis->PesquisaVariavelNivel(mFilhos.at(i)->mostra(), mFilhos.at(i)->GetNivel());
						 if(mNohash == NULL )
						 {
							cout<<"1ERRO: VARIAVEL NAO ENCOTRADA!\n";
							continue;
						 }
						 if( mNohash->GetCategoria() == 2 && this->mIsExpressao == false)
						 	cout<<"ERRO: FUNCAO SOH PODE SER USADA DENTRO DE EXPRESSAO\n";	
						 	continue;
						 
						 
						 //só variáveis podem ser passadas por referência.
						 //TRADUCAO: SE O METODO PEDE PASSAGEM POR REFERENCIA O TIPO DEVE SER VARIAVEL SIMPLES
						 if((mNohash->GetCategoria() == 1 || mNohash->GetCategoria() == 2) && this->mNoCompara->GetPassagem() == 1)
						 {
						 	// exclui o primeiro tokem q eh a propria funcao
						 	if(mNohash->GetId() != this->mNoCompara->GetId())
						 		cout<<"ERRO: só variáveis podem ser passadas por referência.\n";
						 	continue;
						 	
						 }
						 else
						 {
							// pesquisa o nohash 
							if(mNohash->RetTipo() != this->mNoCompara->GetParametros(cont++))
							{
								
								cout<<"CHAMADA DE PROCEDIMENTO/FUNCAO COM VALORES INCOMPATIVEIS\n";
								continue;
							}
						 }
						
					}else
					{
						// se for numero
						if(IsNumero( mFilhos.at(i)->GetTipoLexico() ) )
						{
							// atribui o tipo corrente como numero
							//se tipo corrente for "bool" da erro
							
							if(this->mNoCompara->GetParametros(cont++) != 1)
								cout<<"CHAMADA DE PROCEDIMENTO/FUNCAO COM VALORES INCOMPATIVEIS\n";
							continue;
						}
					}
					continue;
				}	
				
		AnalisaProcedimento(mFilhos.at(i));
	}
}
void Semantico::ExpreRetBool(NoArvore *mPai)
{
			this->mIsExpressao = true;
			//CHAMADA PARA EXPRESSA DENTRO DO IF , FAZER DE FORMA MAIS LEGIVEL
			// OU , CHAMADA PARA EXPRESSA DENTRO DO WHILE , FAZER DE FORMA MAIS LEGIVEL
			this->Expressao(mPai->GetFilhos().at(1));	
			// SE O RETORNO EH DO TIPO BOOLENA
			if(this->mExRetorno != 2)
			{
				cout<<"ERRO: VALOR DE RETORNO INCORRETO PARA COMANDO IF OU WHILE \n";
			}
			this->mIsExpressao = false;
			this->mTipoCorrente = -1;
			this->mRetorno = -1;
			this->mExRetorno = -1;
	
}

//**********TRATA ATRIBUICAO******************
// os fatores presentes numa  EXPRESSAO devem ser sempre do mesmo tipo
// ou todos INTEGER ou todos BOOLEAN
void Semantico::Atribuicao(NoArvore *mPai)
{
	// nivel lexico q analisado
	int mNivelLexico = mPai->GetNivel();

	
	// recebe os filhos do pai
	vector<NoArvore*> mFilhos = mPai->GetFilhos();
	
	
	// analisa cada filho
	for(int i = 0 ; i < mFilhos.size() ;i++)
	{
		
			
		
			// SABER QUAL O TIPO DO IDENTIFICADOR DA ATRIBUICAO
			if(mFilhos.at(i)->IsTerminal() == true )
			{	
				// PESQUISAR NA HASH A VARIAVEL
				// saber qual o tipo lexico
				// 2 EH IDENTIFICADOR
				if (mFilhos.at(i)->GetTipoLexico() == 2 )
				{
					
					//analisar Nohash
					// RETORNO O TIPO DA VARIAVEL "BOOLEAN" , "INTEGER"
					NoHash *mNohash = mHashVariaveis->PesquisaVariavelNivel(mFilhos.at(i)->mostra(), mNivelLexico);	
					
					if(mNohash == NULL )
					{
						cout<<"2ERRO: VARIAVEL NAO ENCOTRADA!\n";
					}
					else
					{
						if(mNohash->GetCategoria() == 0 || mNohash->GetCategoria() == 2)
						{
							// RETORNA INTEGER OU BOOLEAN
							this->mRetorno  = mNohash->RetTipo();
						}
					}
					
					
				}
			}
			//nao pode haver procedimento dentro de expressao
			if(mFilhos.at(i)->mostra().compare("EXPRESSAO") == 0)
			{
				this->mIsExpressao = true;
				Expressao(mFilhos.at(i));
				
				// SE O RETORNO EH DO TIPO BOOLENA
				if(this->mRetorno != this->mExRetorno)
				{
					cout<<"ERRO: VALOR DE RETORNO INCORRETO\n";
				}
				
				this->mIsExpressao = false;
				this->mTipoCorrente = -1;
				this->mRetorno = -1;
				this->mExRetorno = -1;
				continue;
			}
			
		Atribuicao(mFilhos.at(i));
	}	
}


// analisa arvore quando encontrar EXPRESSAO
void Semantico::Expressao(NoArvore *mPai)
{
	// nivel lexico q analisado
	int mNivelLexico = mPai->GetNivel();

	
	// recebe os filhos do pai
	vector<NoArvore*> mFilhos = mPai->GetFilhos();
	
	
	// analisa cada filho
	for(int i = 0 ; i < mFilhos.size() ;i++)
	{
				if( (mFilhos.at(i)->mostra().compare("EXPRESSAO ENTRE PARENTESES") == 0) )
				{
					//RETORNA O METODO COM AS VARIAVEIS sempre num nivel acima
					// um procedimento sempre esta um nivel acima da chamada
					this->mNoCompara = mHashVariaveis->PesquisaVariavelNivel(mFilhos.at(i)->GetFilhos().at(0)->mostra(),mFilhos.at(i)->GetFilhos().at(1)->GetNivel()+1);	
					//analisa a expressao dentro da chamada de procedimento
					cont = 0;
					// analisa cada filho
					AnalisaProcedimento(mFilhos.at(i));
					this->AnalisaNoHash(mNoCompara);
					
					cont = 0;
					continue;
				}
				//se for um terminal deve-se ver o tipo
				if(mFilhos.at(i)->IsTerminal() == true )
				{
					// PESQUISAR NA HASH A VARIAVEL
					// saber qual o tipo lexico
					if (IsIdentificador(mFilhos.at(i)->GetTipoLexico(), mFilhos.at(i)->mostra() ) )
					{
						
						//analisar Nohash
						// RETORNO O TIPO DA VARIAVEL "BOOLEAN" , "INTEGER"
						 NoHash *mNohash = mHashVariaveis->PesquisaVariavelNivel(mFilhos.at(i)->mostra(), mNivelLexico);	
						 if(mNohash == NULL)
						 {
							cout<<"3ERRO: VARIAVEL NAO ENCOTRADA!\n";
						 }
						 else
						 {
						 	
							// pesquisa o nohash 
							this->AnalisaNoHash(mNohash);
						 }
						
					}else
					{
						// se for numero
						if(IsNumero( mFilhos.at(i)->GetTipoLexico() ) )
						{
							// atribui o tipo corrente como numero
							//se tipo corrente for "bool" da erro
							AnalisaNumero();
							//cout<<mFilhos.at(i)->mostra();
						}
					}
					//cout<<this->mExRetorno<<"   "<<endl;
				}	
		Expressao(mFilhos.at(i));
	}
	
	
	
	
}

bool Semantico::IsIdentificador(int mTipoLexico, string mToken)
{
	// DEPENDENDO DO OPERANDO OS VALORES SOH PODEM SER INTEIROS
	if(mToken == "+" || mToken == "-" || mToken == "/" || mToken == "div" )
	{
		// soh pode ser inteiro
		if(this->mTipoCorrente == 2 )
		{
			cout<<"ERRO: IDENTIFICADORES DE TIPO DIFERENTES";
		}else
		{
			// RETORNO EXPRESSAO EH INTEIRO
			// deve setar como inteiro
			if(this->mTipoCorrente == -1)
			{
				this->mTipoCorrente = 1;
				this->mExRetorno = 1;
			}
		}
	}else
	{
		if(mToken == "=" || mToken == "<>" || mToken == "<" || mToken == "<="
		|| mToken == ">="|| mToken == ">" )
		{
			//RETORNO DA EXPRESSAO EH BOOLEAN
			this->mExRetorno = 2;
		}
		
	}
	if (mTipoLexico == 2)
		return true;
	return false;
}

bool Semantico::IsNumero(int mTipoLexico)
{
	if (mTipoLexico == 3)
		return true;
	return false;
}

bool Semantico::AnalisaNoHash(NoHash *mNoHash)
{
	
	switch(mNoHash->GetCategoria())
	{
		case 0:
				VariavelSimples(mNoHash);
				break;
		case 1:
				cout<<"ERRO: PROCEDIMENTO NAO PODE SER USADO EM EXPRESSAO\n";
				break;
		case 2:	
				//funções não podem ser usadas como procedimentos, isto é, devem estar dentro de expressões.
				if(!this->mIsExpressao)
					cout<<"ERRO: FUNCAO NAO PODE SER USADA COMO PROCEDIMENTO\n";
				VariavelSimples(mNoHash);
				break;
		
	}
}
void Semantico::VariavelSimples(NoHash *mNoHash)
{
	if(this->mTipoCorrente == -1)
	{
		this->mTipoCorrente = mNoHash->RetTipo();
		this->mExRetorno = mNoHash->RetTipo();
		
	}else
	{
		if(mNoHash->RetTipo() == this->mTipoCorrente)
			return;
		cout<<"IDENTIFICADORES TIPOS DIFERENTES";
	}
	
}

void Semantico::AnalisaNumero()
{
	// soh pode ser inteiro
		if(this->mTipoCorrente == 2 )
		{
			cout<<"ERRO: IDENTIFICADORES DE TIPO DIFERENTES";
		}else
		{
			// deve setar como inteiro
			if(this->mTipoCorrente == -1)
			{
				this->mExRetorno = 1;
				this->mTipoCorrente = 1;
			}
		}
	
	
}



Semantico::~Semantico()
{
}
